6 분 소요

dash_icon

(4) 콜백(Callbacks) 생성

대화형(Interactive) 구성 요소


이번 장에서는 Dash 레이아웃 app.layout 을 이루는 구성 요소 간 속성 업데이트를 가능하게 하는 콜백 함수(Callbacks) 를 만드는 방법을 다루겠습니다.

일반적으로 프로그래밍에서 콜백 함수란 다른 코드의 인수로서 넘겨주는 함수 형태로 실행 가능한 코드를 의미합니다. 콜백을 넘겨받는 코드는 기존에 정의된 특정 상황이 발생하면 이 콜백 함수를 실행합니다.

Dash 에서는 사용자 입력이 발생하거나 레이아웃에 특정한 변화가 생기면 이 콜백함수를 자동으로 호출해서, 레이아웃을 이루는 구성 요소들의 출력을 업데이트합니다.

따라서 콜백이 구현되지 않은 상태의 Dash 레이아웃은 구성요소들이 독립적으로 작동합니다.

아래는 콜백을 이용해 대화형(Interactive) 구성 요소를 표현하기 위한 간단한 app.py 예제입니다.

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

# Dash 앱 인스턴스 생성
app = dash.Dash(__name__)

# 레이아웃 생성
app.layout = html.Div([
    html.H6("텍스트 박스의 입력에 따라 출력이 바뀌는 것을 확인하세요!"),
    html.Div([
        "Input: ",
        dcc.Input(id='my-input', value='첫번째 입력', type='text')
    ]),
    html.Br(),
    html.Div(id='my-output', children='Default 출력'),

])

# 출력 업데이트 함수 (콜백 데코레이터)
@app.callback(
    Output(component_id='my-output', component_property='children'),
    Input(component_id='my-input', component_property='value')
)
def update_output_div(input_value):
    return 'Output: {}'.format(input_value)


if __name__ == '__main__':
    # Dash 앱 실행
    app.run_server(debug=True)

dash_ex4


Dash 는 레이아웃 구성요소의 입출력과 연결된 함수를 @app.callback 이라는 파이썬 데코레이터로 감싸는 구조로 콜백 함수를 구현합니다. (파이썬 데코레이터 개념은 Decorators 을 참고해 주세요)

위 예제의 Dash 레이아웃은 크게 my-input 이라는 ID의 텍스트 입력 창 구성 요소와, my-output 이라는 ID의 구성 요소로 구성됩니다. 여기서 콜백은 my-input 구성 요소의 값을 Input 으로 받아서, 다시 my-output 구성 요소 출력 값으로 업데이트 해주는 역할을 합니다.


그럼 여기서 어떻게 콜백 매커니즘이 작동하는지 한번 보겠습니다.

  1. @app.callback 데코레이터는 기본적으로 Input 과 Output 속성을 가집니다. Input 은 my-input ID 를 가지는 구성 요소이고, Output 은 my-output ID 를 가지는 구성 요소입니다.
  2. 여기서 Input 구성 요소는 ‘value’ 속성 값을 입력받고, Output 구성 요소는 레이아웃에서 html 태그의 ‘children’ 속성 값을 변경합니다. (component_property 키워드 속성은 생략해도 됩니다)
  3. 만약 dcc.Input 을 통해 입력 받는 ‘value’ 값이 ‘첫번째 입력’ 으로 변경된다면, Dash 레이아웃에서 출력을 담당하는 html.Div(id='my-output', children='Default 출력') 이었던 부분이 html.Div(id='my-output', children='첫번째 입력') 으로 덮어쓰이게 됩니다.


우리가 MS Excel 에서 셀에 함수를 걸어 놓고, 입력 셀에 값을 변경하면 출력 셀의 값이 자동으로 업데이트 되는 원리와 같다고 생각하시면 이해가 쉽습니다. 이렇게 출력 값이 입력 값 변화에 자동으로 반응하는 것을 ‘반응형 프로그래밍(Reactive Programming)’ 이라고도 합니다.

위 예제에서는 children 이라는 HTML 속성을 변경했지만, 같은 방식으로 dcc.Graph 구성 요소의 figure 속성을 변경해서 그래프를 업데이트 하거나, dcc.Dropdown 구성 요소의 optionsstyle 속성을 변경해서 드롭다운에서 보여지는 리스트를 업데이트 하는 것도 가능합니다.

아래는 dcc.Graphdcc.Slider 구성 요소를 콜백 함수로 연동해서 그래프를 슬라이더 값에 따라서 자동 업데이트 하는 예제입니다.

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.express as px
import pandas as pd

# 데이터 로드
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv')

# Dash 앱 인스턴스 생성
app = dash.Dash(__name__)

# 레이아웃 생성
app.layout = html.Div([
    # 그래프 구성 요소
    dcc.Graph(id='graph-with-slider'),
  
    # 슬라이더 구성 요소
    dcc.Slider(
        id='year-slider',
        min=df['year'].min(),
        max=df['year'].max(),
        value=df['year'].min(),
        marks={str(year): str(year) for year in df['year'].unique()},
        step=None
    )
])

# 그래프 출력 업데이트 함수 (콜백 데코레이터)
@app.callback(
    Output('graph-with-slider', 'figure'),
    Input('year-slider', 'value'))
def update_figure(selected_year):
    # 슬라이더 'value' 값에 따라 DataFrame 업데이트
    filtered_df = df[df.year == selected_year]

    # Scatter 그래프 생성
    fig = px.scatter(filtered_df, x="gdpPercap", y="lifeExp",
                     size="pop", color="continent", hover_name="country",
                     log_x=True, size_max=55)
    
    # Scatter 그래프 업데이트
    fig.update_layout(transition_duration=500)

    return fig


if __name__ == '__main__':
    # Dash 앱 실행
    app.run_server(debug=True)
    

dash_ex5


첫번째 예제 방식과 마찬가지로, dcc.Slider 의 ‘value’ 값이 변경될 때마다 Dash 는 새로운 값으로 콜백 함수 update_figure 를 호출합니다. 이 함수는 변경된 값으로 데이터 프레임을 필터링하고, 산점도 객체를 생성하고 Dash 앱에 결과를 반환합니다.

여기서 Dash 콜백 함수를 사용할 때 주의해야 할 점은, df = pd.read_csv('...') 와 같이 Pandas 를 통해 메모리에 데이터 프레임을 로드하는 작업은 콜백 함수 내에 작성하는 것 보다 앱 시작할 때 한번만 실행 될 수 있도록 해주는 것이 좋습니다.

또 콜백은 원본 데이터를 수정하는 것이 아니라 필터링된 데이터를 복사해서 사용합니다. 따라서 콜백을 통해서 전역적으로 선언된 변수 값을 변경하지 않도록 하는 것이 좋습니다. 간단한 예제에서는 문제가 될 경우가 없겠지만, Dash 앱을 실행하는 세션이 여러개일 때 콜백을 통해 전역 상태가 수정되면 다음 세션에 영향을 미치게 됩니다. (만약 Dash 를 통해 원본 데이터셋을 변경하고 싶다면, layout.transition 을 사용하는 방법을 확인해주세요)


본 포스트는 Dash 공식 가이드 를 참고하였습니다.

댓글남기기